/*
    $Header: /Windows/Shell/ShellExt.root/ShellExt/src/CShellExtXML.cpp 1     08-01-05 0:08 Ken $
    $Log: /Windows/Shell/ShellExt.root/ShellExt/src/CShellExtXML.cpp $
 * 
 * 1     08-01-05 0:08 Ken
*/



#include "stdafx.h"

#include "CShellExtXML.h"
#include "CJunction.h"
#include "Misc.h"


const TCHAR CShellExtXML::s_szXMLInitStr[]={_T(\
"<?xml version=\"1.0\"?>\r\n\
<!-- ShellExt operation XML file -->\r\n\
<ShellExtXML>\r\n\
</ShellExtXML>")};

pfnCreateDoXMLCbIns CShellExtXML::s_pfnCreateDoXMLCbIns[]=
{
    CDoXMLCbCMD::CreateDoXMLCbCMDIns,
    CDoXMLCbJunct::CreateDoXMLCbJunctIns,
};

int CShellExtXML::s_IDoXMLCbNumb=sizeof(CShellExtXML::s_pfnCreateDoXMLCbIns)/sizeof(CShellExtXML::s_pfnCreateDoXMLCbIns[0]);


//////////////////////////////////////
//  CDoXMLCbCMD
//

CShellExtXML::CShellExtXML()
{
    m_pIDoXMLCbS=new IDoXMLCb*[s_IDoXMLCbNumb];
    for(int i=0; i<s_IDoXMLCbNumb; i++)
    {
        m_pIDoXMLCbS[i]=(*s_pfnCreateDoXMLCbIns[i])();
    }
}

CShellExtXML::~CShellExtXML()
{
    for(int i=0; i<s_IDoXMLCbNumb; i++)
    {
        delete m_pIDoXMLCbS[i];
    }

    delete[] m_pIDoXMLCbS;
}

BOOL CShellExtXML::Init(const TCHAR* pszPath, BOOL fForeceCreate)
{
    if(NULL==pszPath)
    {
        return FALSE;
    }

    do
    {
    // create and set DOM attributes
        if(FAILED(m_ptrDoc.CreateInstance(__uuidof(DOMDocument30))))
        {
            ATLTRACE(_T("CoCreateInstance faild!\r\n"));
            break;
        }

        m_ptrDoc->async=VARIANT_FALSE;
        m_ptrDoc->validateOnParse=VARIANT_FALSE;
        m_ptrDoc->resolveExternals=VARIANT_FALSE;
        m_ptrDoc->preserveWhiteSpace=VARIANT_FALSE;


        // load xml file
        // no such xml file or invalid.
        if (VARIANT_FALSE==m_ptrDoc->load(pszPath))
        {
            if(FALSE!=fForeceCreate)
            {
                ATLVERIFY(VARIANT_FALSE!=m_ptrDoc->loadXML(s_szXMLInitStr));
                if(FAILED(m_ptrDoc->save(pszPath)))
                {
                    ATLTRACE(_T("save faild!\r\n"));
                    break;
                }
            }
            else
            {
                ATLTRACE(_T("load %s faild!\r\n"), pszPath);
                break;
            }
        }

        // use xpath for select
        m_ptrDoc->setProperty("SelectionLanguage", "XPath");
        // Set the selection namespace URI if the nodes you wish to select later use a namespace prefix
        m_ptrDoc->setProperty("SelectionNamespaces", "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'");

        // check root element name
        m_ptrNodeRoot=m_ptrDoc->documentElement;
        USES_CONVERSION;
        if( (m_ptrNodeRoot==NULL)
            ||(0!=_tcsicmp(OLE2T(m_ptrNodeRoot->nodeName),_T("ShellExtXML"))) )
        {
            ATLTRACE(_T("invalid XML file:%s!\r\n"), pszPath);
            break;
        }
    }while(0);

    if(m_ptrNodeRoot==NULL)
    {
        return FALSE;
    }

    _tcscpy_s(m_szShellExtXMLFile, pszPath);
    return TRUE;
}

BOOL CShellExtXML::GetCmdInfo(VOID* Hnd, const TCHAR* pszName, _variant_t& vtValue )
{
    MSXML2::IXMLDOMNodePtr  ptrNodeCmd((MSXML2::IXMLDOMNode*)Hnd);

    MSXML2::IXMLDOMNamedNodeMapPtr ptrAttrs= ptrNodeCmd->attributes;

    MSXML2::IXMLDOMNodePtr  ptrNodeAttr=ptrAttrs->getNamedItem(pszName);
    if(ptrNodeAttr==NULL)
    {
        ATLTRACE(_T("can't get attribute:%s!\r\n"), pszName);
        return FALSE;
    }

    vtValue=ptrNodeAttr->nodeValue;

    return TRUE;
}

VOID* CShellExtXML::BgnSetCmdInfo(const TCHAR* pszCmdCat)
{
    MSXML2::IXMLDOMNodePtr  ptrNodeCat=GetChildNodeForece(m_ptrNodeRoot, pszCmdCat);
    MSXML2::IXMLDOMElementPtr* pptrEle=new MSXML2::IXMLDOMElementPtr( m_ptrDoc->createElement(_T("Parameter")) );
    ptrNodeCat->appendChild(*pptrEle);

    // for beauty format
    AddWhiteSpaceToNode( ptrNodeCat, true, 2);
    return pptrEle;
}

BOOL CShellExtXML::SetCmdInfo(VOID* Hnd, const TCHAR* pszPara, _variant_t& vtValue)
{
    MSXML2::IXMLDOMElementPtr* pPtrEle=(MSXML2::IXMLDOMElementPtr*)Hnd;
    (*pPtrEle)->setAttribute( pszPara, vtValue );
    return TRUE;
}

VOID CShellExtXML::EndSetCmdInfo(VOID* Hnd)
{
    delete (MSXML2::IXMLDOMElementPtr*)Hnd;
    m_ptrDoc->save(m_szShellExtXMLFile);
}

VOID CShellExtXML::DoShellExtXML()
{
    MSXML2::IXMLDOMNodeListPtr ptrNodeRootLst=m_ptrNodeRoot->childNodes;
    for(int i=0; i<ptrNodeRootLst->length; i++)
    {
        MSXML2::IXMLDOMNodePtr  ptrNodeCat=ptrNodeRootLst->item[i];
        if(MSXML2::NODE_ELEMENT==ptrNodeCat->nodeType)
        {
            for(int j=0; j<s_IDoXMLCbNumb; j++)
            {
                if(0==_tcscmp(ptrNodeCat->nodeName, m_pIDoXMLCbS[j]->VGetCatalog()))
                {
                    MSXML2::IXMLDOMNodeListPtr ptrNodeCmdLst=ptrNodeCat->childNodes;
                    for(int k=0; k<ptrNodeCmdLst->length; k++)
                    {
                        MSXML2::IXMLDOMNodePtr  ptrNodeCmd=ptrNodeCmdLst->item[k];
                        try{
                            //
                            //4 !!! ptrNodeCmd is a _com_ptr_t and has member operator&, it will release the real pointer
                            //  so use operator Interface*() member to get the real pointer and convert then transfer
                            //if(FALSE!=((*it)->pfn)((*it)->pvCnt, &ptrNodeCmd ) )
                            if(FALSE!=m_pIDoXMLCbS[j]->VOnXMLCmd((MSXML2::IXMLDOMNode*)ptrNodeCmd ) )
                            {
                                ptrNodeCat->removeChild(ptrNodeCmd);
                            }
                        }
                        catch(...)
                        {
                            ATLTRACE(_T("DoShellExtXML call exception!\r\n"));
                        }
                    }

                    break;
                }
            }
        }
    }

    m_ptrDoc->save(m_szShellExtXMLFile);
}

MSXML2::IXMLDOMNodePtr  CShellExtXML::GetChildNodeForece(MSXML2::IXMLDOMNodePtr & ptrNodeParent, const TCHAR*  pszChld)
{
    MSXML2::IXMLDOMNodePtr  ptrNodeChild=ptrNodeParent->selectSingleNode(pszChld);
    if(ptrNodeChild==NULL)
    {
        ptrNodeChild=m_ptrDoc->createElement(pszChld);
        ptrNodeParent->appendChild(ptrNodeChild);
        AddWhiteSpaceToNode(ptrNodeParent, true, 2);
        AddWhiteSpaceToNode(ptrNodeChild, true, 2);
    }

    return ptrNodeChild;
}

// Helper function to append newline and tabs after pNode
VOID CShellExtXML::AddWhiteSpaceToNode(MSXML2::IXMLDOMNodePtr & ptrNode, bool fNewLine, int nTabs)
{
    TCHAR szSpace[256]={0};
    TCHAR* pszSpace=szSpace;

    if(fNewLine)
    {
        szSpace[0]='\r';
        szSpace[1]='\n';
        pszSpace=&szSpace[2];
    }

    for(int i=0; i<nTabs; i++)
    {
        pszSpace[i]='\t';
    }

    MSXML2::IXMLDOMNodePtr  ptrNodeSpace=m_ptrDoc->createTextNode(szSpace);
    ptrNode->appendChild(ptrNodeSpace);
 }


//////////////////////////////////////
//  CDoXMLCbCMD
//

BOOL CDoXMLCbCMD::VOnXMLCmd(VOID* Hnd)
{
    _variant_t vtCommand;
    if(FALSE==CShellExtXML::GetCmdInfo(Hnd, _T("Command"), vtCommand))
    {
        return FALSE;
    }

    _variant_t vtCmdLine;
    if(FALSE==CShellExtXML::GetCmdInfo(Hnd, _T("CommandLine"), vtCmdLine))
    {
        return FALSE;
    }

    _variant_t vtShow;
    if(FALSE==CShellExtXML::GetCmdInfo(Hnd, _T("Show"), vtCmdLine))
    {
        vtShow=SW_SHOW;
    }

    ShellExecute(NULL, _T("open"),  (_bstr_t)vtCommand, (_bstr_t)vtCmdLine, NULL, vtShow);

    return TRUE;
}


//////////////////////////////////////
//  CDoXMLCbJunct
//

BOOL CDoXMLCbJunct::VOnXMLCmd(VOID* Hnd)
{
    _variant_t vtJunctPath;
    if(FALSE==CShellExtXML::GetCmdInfo(Hnd, _T("JunctPath"), vtJunctPath))
    {
        return FALSE;
    }

    _variant_t vtJunctTarget;
    if(FALSE==CShellExtXML::GetCmdInfo(Hnd, _T("JunctTarget"), vtJunctTarget))
    {
        return FALSE;
    }

    //
    //4 !!! this will generate a temp _bstr_t and pszJunctPath pointer to it,
    // and then the temp _bstr_t will be destroy, so pszJunctPath point to a invalide address
    // TCHAR* pszJunctPath=(_bstr_t)vtJunctPath;
    _bstr_t bstrJunctPath=vtJunctPath;
    if( PathFileExists(bstrJunctPath) )
    {
        TCHAR szInfo[512];
        _stprintf_s(szInfo, ARRAYOF(szInfo), _T("%s exist, create junction will lose all data!\r\nDo you want continue?\r\n"), (TCHAR)vtJunctPath);
        if(IDOK!=MessageBox(NULL, szInfo, _T("Delete junction error!"), MB_OKCANCEL))
        {
            return FALSE;
        }
    }

    if(FALSE==CJunction::CreateJunction(bstrJunctPath, (_bstr_t)vtJunctTarget, TRUE))
    {
        return FALSE;
    }

    return TRUE;
}

